home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 June: Reference Library / Dev.CD Jun 00 RL Disk 1.toast / pc / technical documentation / develop / develop issue 28 / develop issue 28 code / merge tools / merge.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-25  |  29.6 KB  |  889 lines

  1. /* Test routines for Eclectus integration utilities.
  2.      Copyright (C) 1992-1996 Eclectus (D. John Anderson, Alan B. Harper).
  3.  
  4. This file is part of the Eclectus integration utilities.
  5.  
  6. Eclectus integration utilities are free software; you can redistribute
  7. it and/or modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 1, or
  9. (at your option) any later version.
  10.  
  11. Eclectus integration utilities is distributed in the hope that it
  12. will be useful, but WITHOUT ANY WARRANTY; without even the implied
  13. warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. See the GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with the Eclectus integration utilities; see the file COPYING.
  18. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge,
  19. MA 02139, USA.    */
  20.  
  21. #include "diff.h"
  22. #include <limits.h>
  23. #include <stddef.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26.  
  27. #ifdef DF_MACHINE_MACINTOSH
  28.     #include <Types.h>
  29.     #include <CursorCtl.h>
  30. #endif
  31.  
  32. /*
  33.  * I put the following array in a struct since I later constuct an array of
  34.  * nameType elements in nameListType.  This was done to work around a bug in
  35.  * the MPW compiler which doesn't properly handle taking an address of an
  36.  * array of arrays.  I suspect this relates to the fact that the designers
  37.  * of C didn't understand types (i.e. arrays are different from other types
  38.  * like structs).
  39.  */
  40.  
  41. typedef struct nameType {
  42.     char name [FILENAME_MAX + 1];
  43. } nameType, *namePType;
  44.  
  45. /*
  46.  * nameListType is a variable length structure with numberOfItems in the nameData
  47.  * array.  Unfortunately ANSI C requires a single element in this array making
  48.  * the syntax of variable length types extremely awkward.
  49.  */
  50.  
  51. typedef struct nameListType {
  52.     unsigned int                    numberOfItems;
  53.     nameType                            nameData[1];
  54. } nameListType, *nameListPType;
  55.  
  56. #define ADDED    1
  57. #define REMOVED 2
  58.  
  59. static const char          *programNamePtr;
  60. static int                            showNonCollisions; /* Show all changes, even when they don't collide */
  61.  
  62. static nameListPType        CommandLine (int argc, const char **argv);
  63.     static void                         CommandLineError (int errorMessage, const char *theString);
  64. int __cdecl                         main (int argc, const char **argv);
  65. static int                            MergeFiles (struct file_data *fileListPtr,
  66.                                                                         FILE *outFilePtr);
  67. static void                         MergePaths (nameListPType pathListPtr);
  68. static void                                PrependStringToPathName (char *destinationCharPtr, const char *prefixCharPtr);
  69. static void                         PrintEndFileName (FILE *outFilePtr);
  70. static void                         PrintLines (FILE *outFilePtr,
  71.                                                                         struct file_data *filePtr,
  72.                                                                         int startLineNumber,
  73.                                                                         register int endLineNumber);
  74. static void                         PrintStartFileName (FILE *outFilePtr, struct file_data *filePtr);
  75. static int __cdecl            StrPtrCmp (char **string0PtrPtr, char **string1PtrPtr);
  76.  
  77. nameListPType
  78. CommandLine (int argc, const char **argv)
  79. /*
  80.  * Parses the command line.  Returns a pointer to a nameListType.  Dealing with variable
  81.  * length items is such a pain in the ass in C.  The caller is responsible for
  82.  * freeing this storage.
  83.  */
  84. {
  85.     char                                    commandChar;
  86.     register const char  *commandStringPtr;
  87.     const char                     *lastNamePtr;
  88.     int                                     nameListLength;
  89.     int                                     nameListTotalSpace;
  90.     namePType                         namePtr;
  91.     nameListPType                 pathListPtr;
  92.  
  93.     lastNamePtr = NULL;
  94. /*
  95.  * Set nameListLength to the size of a name list with no elements in it.  Since ANSI
  96.  * C requires us to put one item in the array we use offsetof to get the size.
  97.  */
  98.     nameListLength = offsetof (nameListType, nameData);
  99.     nameListTotalSpace = offsetof (nameListType, nameData) + (3 * sizeof (nameType));
  100.     pathListPtr = xmalloc (nameListTotalSpace);
  101.     pathListPtr->numberOfItems = 0;
  102.  
  103.     programNamePtr = *argv++;
  104.     argc--;
  105.  
  106. /* Parse the commands and make sure they are legal */
  107.  
  108.     while (argc > 0) {
  109.         commandStringPtr = *argv++;
  110.         argc--;
  111.         if (*commandStringPtr != '-') {
  112.             if (nameListLength >= nameListTotalSpace) {
  113.                 nameListTotalSpace = nameListLength + (4 * sizeof (nameType));
  114.                 pathListPtr = xrealloc (pathListPtr, nameListTotalSpace);
  115.             }
  116.             namePtr = (namePType) (((char *) pathListPtr) + nameListLength);
  117.             memcpy (namePtr->name, commandStringPtr, strlen (commandStringPtr) + 1);
  118.  
  119.             if (lastNamePtr != NULL && FileType (lastNamePtr) == BAD_FILE_TYPE)
  120.                 ErrorWithStringArgument (CANT_OPEN_FILE, lastNamePtr);
  121.             lastNamePtr = commandStringPtr;
  122.         
  123.             nameListLength += sizeof (nameType);
  124.             pathListPtr->numberOfItems ++;
  125.         } else {
  126.             if (strlen (commandStringPtr) != 2)
  127.                 CommandLineError (UNEXPECTED_OPTION, commandStringPtr);
  128.             commandChar = *(commandStringPtr + 1);
  129.             switch (commandChar) {
  130.                 case ('c'):
  131.                     showNonCollisions = 1;
  132.                     break;
  133.                 #ifdef DF_DEBUG
  134.                     case ('X'):
  135.                         ECCheckFreedBlocks = 1;
  136.                         break;
  137.                     case ('x'):
  138.                         ECHeapCheckOn = 1;
  139.                         break;
  140.                 #endif
  141.                 default:
  142.                     CommandLineError (UNEXPECTED_OPTION, commandStringPtr);
  143.                     break;
  144.             }
  145.         }
  146.     }
  147.     if (pathListPtr->numberOfItems < 3)
  148.         CommandLineError (THREE_OR_MORE_MERGE_FILES, NULL);
  149.  
  150.     pathListPtr = xrealloc (pathListPtr, nameListLength);
  151.     return (pathListPtr);
  152. }
  153.  
  154.     void
  155.     CommandLineError (int errorMessage, const char *theString)
  156.     /*
  157.      * Part of CommandLine.
  158.      */
  159.     {
  160.         fprintf (stderr, "# - ");
  161.         if (theString != NULL)
  162.             fprintf (stderr, ECMessagessArray [errorMessage], theString);
  163.         else
  164.             fprintf (stderr, ECMessagessArray [errorMessage], "");
  165.         fprintf (stderr, ".\n\n"
  166.                                          "### Merge version %s\n"
  167.                                          "#usage: %s [-c] rootPath modification1Path [modification2Path]"
  168.                                          " ... destinationPath \n"
  169.                                          "        -c shows all changes even if no collisions occur\n\n",
  170.                                          VERSION_STRING,
  171.                                          programNamePtr);
  172.         exit (EXIT_FAILURE);
  173.     }
  174.  
  175. int
  176. __cdecl main (int argc, const char **argv)
  177. {
  178.     #ifdef DF_DEBUG
  179.         ECUns32            allocatedBytes;
  180.     #endif
  181.     nameListPType pathListPtr;
  182.  
  183.     pathListPtr = CommandLine (argc, argv);
  184.     MergePaths (pathListPtr);
  185.     free (pathListPtr);
  186.  
  187.     #ifdef DF_DEBUG
  188.         allocatedBytes = ECDebugPrintMemoryUsage (stderr);
  189.         if (allocatedBytes != 0) {
  190.             fprintf (stderr, "\n\n"
  191.                                              "MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!\n"
  192.                                              "MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!\n"
  193.                                              "MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!\n"
  194.                                              "MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!\n"
  195.                                              "MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!  MEMORY LEAK!\n"
  196.                                              "\n\n");
  197.         }
  198.     #endif
  199.     return (0);
  200. }
  201.  
  202. int
  203. MergeFiles (struct file_data *fileListPtr, FILE *outFilePtr)
  204. /*
  205.  * Merges a list of files into the first item of the list and writes the result to
  206.  * outFilePtr.    Returns 1 if changes overlap (i.e. collide) and must be hand integrated.
  207.  * Returns -1 if there were no differences between any of the files.  Returns 0 otherwise.
  208.  */
  209. {
  210.     int                                                 collision;
  211.     struct line_def                      *firstLinePtr;
  212.     struct change                          *firstLowLimitPtr;
  213.     register struct file_data     *firstMatchFilePtr;
  214.     register struct file_data     *filePtr;
  215.     struct file_data                     *firstScriptFilePtr;
  216.     int                                                 highLimit;
  217.     int                                                 highLimitInFile;
  218.     struct change                          *highLimitPtr;
  219.     int                                                 lineCount;
  220.     int                                                 lineNumber;
  221.     struct line_def                      *linePtr;
  222.     int                                                 lowLimit;
  223.     int                                                 lowLimitInFile;
  224.     struct change                          *lowLimitPtr;
  225.     int                                                 nextHighLimit;
  226.     struct change                          *nextHighLimitPtr;
  227.     int                                                 nextLowLimit;
  228.     int                                                 overlap;
  229.     int                                                 printedOneCopy;
  230.     register struct file_data     *rootFilePtr;
  231.     int                                                 rootLineNumber;
  232.     int                                                 showFileName;
  233.  
  234.     collision = 0;
  235.     rootLineNumber = 0;
  236.     /*
  237.      * Calculate 2 way diffs from all the files and then merge the 2 way diffs.
  238.      */
  239.     rootFilePtr = fileListPtr;
  240.     firstScriptFilePtr = rootFilePtr->nextFilePtr;
  241.     filePtr = firstScriptFilePtr;
  242.     while (filePtr != NULL) {
  243.         diff_2_files (rootFilePtr, filePtr);
  244.         lowLimitPtr = filePtr->scriptPtr;
  245.         filePtr->lowLimitPtr = lowLimitPtr;
  246.         filePtr->highLimitPtr = lowLimitPtr;
  247.         filePtr = filePtr->nextFilePtr;
  248.     }
  249.  
  250.     do {
  251.      /*
  252.         * Find the lowest range of blocks that overlap.  This code is tricky.  We pass through
  253.         * all the files accumulating a low and high limit of the lowest overlapping changes in
  254.         * the root.  It gets tricky because more than one block may be contained in the lowest
  255.         * overlapping range and because as the range expands (as you pass over the files) you
  256.         * may be required to reexamine previous files to pick up previously excluded blocks that
  257.         * are now included in the expanded range. 
  258.         */
  259.         lowLimit = INT_MAX;
  260.         highLimit = INT_MAX;
  261.         overlap = FALSE;
  262.  
  263. SetHighLimitAndReExaminePreviousFiles:
  264.         filePtr = firstScriptFilePtr;
  265.         while (filePtr != NULL) {
  266.             lowLimitPtr = filePtr->lowLimitPtr;
  267.             if (lowLimitPtr != NULL) {
  268.                 lowLimitInFile = lowLimitPtr->line0 /* where to begin deleting */;
  269.                 highLimitInFile = lowLimitInFile + filePtr->highLimitPtr->deleted /* where to end adding */;
  270.                 #if 0 /* comment in when testing */
  271.                     printf ("inserted: %d deleted: %d line0: %d line1: %d file: \"%s\" lowLimitInFile: %d highLimitInFile: %d\n",
  272.                                      lowLimitPtr->inserted, lowLimitPtr->deleted, lowLimitPtr->line0, lowLimitPtr->line1,
  273.                                      filePtr->namePtr, lowLimitInFile, highLimitInFile);
  274.                 #endif
  275.                 /*
  276.                  * Count non-overlapping empty identical ranges as overlaps
  277.                  */
  278.                 if (highLimit == lowLimit && highLimit == highLimitInFile && highLimit == lowLimitInFile)
  279.                  overlap = TRUE;
  280.                 else
  281.                 /*
  282.                  * Exclude blocks above the range
  283.                  */
  284.                     if (lowLimitInFile < highLimit) {
  285.                         if (highLimitInFile < lowLimit) {
  286.                         /*
  287.                          * We have a non-overlapping block below all others
  288.                          */
  289.                             lowLimit = lowLimitInFile;
  290.                             highLimit = highLimitInFile;
  291.                         } else {
  292.                             /*
  293.                              * We have a overlapping block, which if it extends the highLimit will open the possibility
  294.                              * of overlaps in files that we have already processed.  This requires that we reexamine
  295.                              * previous files.
  296.                              */
  297.                             overlap = TRUE;
  298.                             if (lowLimitInFile < lowLimit)
  299.                                 lowLimit = lowLimitInFile;
  300.                             if (highLimitInFile > highLimit) {
  301.                                 highLimit = highLimitInFile;
  302.                                 #if 0 /* comment in when testing */
  303.                                     printf ("lowLimit: %d highLimit: %d\n", lowLimit, highLimit);
  304.                                 #endif
  305.                                 goto SetHighLimitAndReExaminePreviousFiles;
  306.                             } else {
  307.                                 /*
  308.                                  * We need to check the possibility of subsequent blocks in this file falling between
  309.                                  * lowLimit and highLimit.
  310.                                  */
  311.                                 nextHighLimitPtr = filePtr->highLimitPtr;
  312.                                 do {
  313.                                     nextHighLimitPtr = nextHighLimitPtr->link;
  314.                                     if (nextHighLimitPtr == NULL /* i.e. no subsequent blocks */)
  315.                                         break;
  316.                                     nextLowLimit = nextHighLimitPtr->line0;
  317.                                     if (nextLowLimit >= highLimit /* i.e. subsequent block doesn't overlap */)
  318.                                         break;
  319.                                  /*
  320.                                     * Subsequent block overlaps.    Include it in the list of blocks within the range.
  321.                                     * Then consider the possibility that the block extended the highLimit and requires
  322.                                     * that we reexamine previous files.
  323.                                     */
  324.                                     filePtr->highLimitPtr = nextHighLimitPtr;
  325.                                     nextHighLimit = nextLowLimit + nextHighLimitPtr->deleted;
  326.                                     if (nextHighLimit > highLimit) {
  327.                                         highLimit = nextHighLimit;
  328.                                         #if 0 /* comment in when testing */
  329.                                             printf ("lowLimit: %d highLimit: %d\n", lowLimit, highLimit);
  330.                                         #endif
  331.                                         goto SetHighLimitAndReExaminePreviousFiles;
  332.                                     }
  333.                                 } while (TRUE);
  334.                             }
  335.                         }
  336.                     }
  337.             }
  338.             #if 0 /* comment in when testing */
  339.                 printf ("lowLimit: %d highLimit: %d\n", lowLimit, highLimit);
  340.             #endif
  341.             filePtr = filePtr->nextFilePtr;
  342.         }
  343.         /*
  344.          * We've got the range of changes.  If there aren't any more changes then don't write them and
  345.          * exit the loop.    Next check for identical changes that overlap.
  346.          */
  347.         if (lowLimit == INT_MAX)
  348.             break;
  349.  
  350.         if (overlap) {
  351.             firstMatchFilePtr = NULL;
  352.             filePtr = firstScriptFilePtr;
  353.             while (filePtr != NULL) {
  354.                 lowLimitPtr = filePtr->lowLimitPtr;
  355.                 highLimitPtr = filePtr->highLimitPtr;
  356.                 if (lowLimitPtr != NULL) {
  357.                     highLimitInFile = INT_MAX;
  358.                     if (highLimitPtr != NULL)
  359.                         highLimitInFile = lowLimitPtr->line0 + highLimitPtr->deleted;
  360.                     if (highLimitInFile <= highLimit) {
  361.                         if (firstMatchFilePtr == NULL)
  362.                             firstMatchFilePtr = filePtr;
  363.                         else {
  364.                             firstLowLimitPtr = firstMatchFilePtr->lowLimitPtr;
  365.                             do {
  366.                                 if (firstLowLimitPtr == NULL ||
  367.                                         firstLowLimitPtr->deleted != lowLimitPtr->deleted ||
  368.                                         firstLowLimitPtr->line0 != lowLimitPtr->line0 ||
  369.                                         firstLowLimitPtr->inserted != lowLimitPtr->inserted)
  370.                                         goto ChangesDontMatch;
  371.                                 lineCount = firstLowLimitPtr->inserted;
  372.                                 firstLinePtr = &firstMatchFilePtr->linbuf[firstLowLimitPtr->line1];
  373.                                 linePtr = &filePtr->linbuf[lowLimitPtr->line1];
  374.                                 while (lineCount > 0) {
  375.                                     if (firstLinePtr->length != linePtr->length ||
  376.                                             memcmp (firstLinePtr->text, linePtr->text, linePtr->length) != 0)
  377.                                         goto ChangesDontMatch;
  378.                                     firstLinePtr ++;
  379.                                 linePtr ++;
  380.                                     lineCount--;
  381.                                 }
  382.                                 lowLimitPtr = lowLimitPtr->link;
  383.                                 firstLowLimitPtr = firstLowLimitPtr->link;
  384.                             } while (lowLimitPtr != NULL && highLimit > lowLimitPtr->line0);
  385.                             if (firstLowLimitPtr != NULL && highLimit > firstLowLimitPtr->line0)
  386.                                 goto ChangesDontMatch;
  387.                         }
  388.                     }
  389.                 }
  390.                 filePtr = filePtr->nextFilePtr;
  391.             }
  392.             overlap = FALSE;
  393.         }
  394. ChangesDontMatch:
  395.         /*
  396.          * Write the changes.
  397.          */
  398.         PrintLines (outFilePtr, rootFilePtr, rootLineNumber, lowLimit);
  399.         if (highLimit > lowLimit && showNonCollisions) {
  400.                 PrintStartFileName (outFilePtr, rootFilePtr);
  401.                 PrintLines (outFilePtr, rootFilePtr, lowLimit, highLimit);
  402.                 PrintEndFileName (outFilePtr);
  403.         }
  404.         showFileName = showNonCollisions || overlap;
  405.         filePtr = firstScriptFilePtr;
  406.         printedOneCopy = FALSE;
  407.         while (filePtr != NULL) {
  408.             lowLimitPtr = filePtr->lowLimitPtr;
  409.             highLimitPtr = filePtr->highLimitPtr;
  410.             if (lowLimitPtr != NULL) {
  411.                 highLimitInFile = INT_MAX;
  412.                 if (highLimitPtr != NULL)
  413.                     highLimitInFile = lowLimitPtr->line0 + highLimitPtr->deleted;
  414.                 if (highLimitInFile <= highLimit) {
  415.                     if (showFileName)
  416.                         PrintStartFileName (outFilePtr, filePtr);
  417.                     lineNumber = lowLimit;
  418.                     do {
  419.                         if (showFileName || !printedOneCopy) {
  420.                             PrintLines (outFilePtr, rootFilePtr, lineNumber, lowLimitPtr->line0);
  421.                             PrintLines (outFilePtr, filePtr, lowLimitPtr->line1, lowLimitPtr->line1 + lowLimitPtr->inserted);
  422.                         }
  423.                         lineNumber = lowLimitPtr->line0 + lowLimitPtr->deleted;
  424.                         if (highLimitPtr == lowLimitPtr)
  425.                             highLimitPtr = highLimitPtr->link;
  426.                         lowLimitPtr = lowLimitPtr->link;
  427.                     } while (lowLimitPtr != NULL && highLimit > lowLimitPtr->line0);
  428.                     if (showFileName || !printedOneCopy)
  429.                         PrintLines (outFilePtr, rootFilePtr, lineNumber, highLimit);
  430.                     if (showFileName)
  431.                         PrintEndFileName (outFilePtr);
  432.                     filePtr->lowLimitPtr = lowLimitPtr;
  433.                     filePtr->highLimitPtr = highLimitPtr;
  434.                     printedOneCopy = TRUE;
  435.                 }
  436.             }
  437.             if (overlap)
  438.                 collision = 1;
  439.             filePtr = filePtr->nextFilePtr;
  440.         }
  441.         rootLineNumber = highLimit;
  442.     } while (TRUE);
  443.     PrintLines (outFilePtr, rootFilePtr, rootLineNumber, INT_MAX);
  444.  
  445.     if (rootLineNumber == 0)
  446.         collision = -1;
  447.     return (collision);
  448. }
  449.  
  450. void
  451. MergePaths (nameListPType pathListPtr)
  452. {
  453.     char                                             *baseBuffer;
  454.     unsigned int                                baseBufsize;
  455.     register char                             *charPtr;
  456.     char                                            **charPtrPtr;
  457.     int                                                 collision;
  458.     char                                             *collisionFormatString;
  459.     char                                                collisionName[FILENAME_MAX + 1];
  460.     int                                                 count;
  461.     int                                                 currentFileType;
  462.     FILE                                             *destinationFileDescriptor;
  463.     fileNameListPType                     directoryStoragePtr;
  464.     int                                                    fileAttributes;
  465.     FILE                                             *fileDescriptor;
  466.     struct file_data                     *fileListPtr;
  467.     char                                             *fileModeCharPtr;
  468.     register struct file_data     *filePtr;
  469.     int                                                    filesThatDifferFromBase;
  470.     char                                             *fileTypeString;
  471.     struct file_data                     *fileWithDataPtr;
  472.     int                                                    incompleteMerge;
  473.     char                                             *incompleteMergeString;
  474.     int                                                 ioResult;
  475.     int                                                    isBinaryFile;
  476.     char                                             *lastFileNamePtr;
  477.     int                                                 lastFileType;
  478.     register namePType                    namePtr;
  479.     int                                                 namesArrayLength;
  480.     char                                            **namesArrayPtr;
  481.     int                                                 namesArrayTotalSpace;
  482.     int                                                 newNamesArrayLength;
  483.     char                                            **nextCharPtrPtr;
  484.     struct file_data                     *nextFilePtr;
  485.     int                                                 sourceFileCount;
  486.     char                                             *suffixCharPtr;
  487.     int                                                    version;
  488.     char                                                versionString [12 + LENGTH_OF_COLLISION_PREFIX];
  489.  
  490.     #ifdef DF_MACHINE_MACINTOSH
  491.         SpinCursor (1);
  492.     #endif
  493.     
  494.      destinationFileDescriptor = NULL;
  495.     fileListPtr = NULL;
  496.     collision = FALSE;
  497.     sourceFileCount = pathListPtr->numberOfItems - 1 /* minus destination path */;
  498.  
  499.     count = pathListPtr->numberOfItems - 1 /* minus destination path */;
  500.     namePtr = pathListPtr->nameData  /* start with root */;
  501.     lastFileType = BAD_FILE_TYPE;
  502.     lastFileNamePtr = NULL /* initialize for debugging */;
  503.  
  504.     while (count > 0) {
  505.         currentFileType = FileType (namePtr->name);
  506.         if (currentFileType != BAD_FILE_TYPE) {
  507.             if (lastFileType != BAD_FILE_TYPE && lastFileType != currentFileType) {
  508.                 fprintf (stderr, "%s\n%s\n", lastFileNamePtr, namePtr->name);
  509.                 Error (MIXED_FILE_TYPES_DONT_MERGE);
  510.             }
  511.             lastFileType = currentFileType;
  512.             lastFileNamePtr = namePtr->name;
  513.         }
  514.         count--;
  515.         namePtr ++;
  516.     }
  517.     switch (lastFileType) {
  518.         case (DIRECTORY_TYPE):
  519.             namePtr = pathListPtr->nameData;
  520.             count = sourceFileCount;
  521.     
  522.             namesArrayTotalSpace = (256 * sizeof (char *));
  523.             namesArrayPtr = xmalloc (namesArrayTotalSpace + sizeof (char *) /* for NULL termination */);
  524.             namesArrayLength = 0;
  525.             filePtr = NULL;
  526.     
  527.             do {
  528.                 directoryStoragePtr = ReadDirectory (namePtr->name);
  529.                 if (directoryStoragePtr != NULL) {
  530.                     nextFilePtr = xmalloc (sizeof (struct file_data));
  531.                     bzero (nextFilePtr, sizeof (struct file_data));
  532.                     nextFilePtr->buffer = (char *) directoryStoragePtr;
  533.                     if (fileListPtr == NULL)
  534.                         fileListPtr = nextFilePtr;
  535.                     else
  536.                         filePtr->nextFilePtr = nextFilePtr;
  537.                     filePtr = nextFilePtr;
  538.                     charPtr = directoryStoragePtr->names;
  539.                     newNamesArrayLength = namesArrayLength + (sizeof (char *) * directoryStoragePtr->numberOfItems);
  540.                     if (newNamesArrayLength > namesArrayTotalSpace) {
  541.                         namesArrayTotalSpace = newNamesArrayLength + (128 * sizeof (char *));
  542.                         namesArrayPtr = xrealloc (namesArrayPtr, namesArrayTotalSpace + sizeof (char *)/* for NULL termination */);
  543.                     }
  544.                     charPtrPtr = (char **) ((char *) namesArrayPtr + namesArrayLength);
  545.                     while (*charPtr != '\0') {
  546.                         *charPtrPtr++ = charPtr;
  547.                         charPtr += strlen (charPtr) + 1;
  548.                     }
  549.                     namesArrayLength = newNamesArrayLength;
  550.                 }
  551.                 count --;
  552.                 namePtr++;
  553.             } while (count > 0);
  554.             charPtrPtr = (char **) ((char *) namesArrayPtr + namesArrayLength);
  555.             *charPtrPtr = NULL;
  556.     
  557.             ioResult = MakeDirectory (namePtr->name);
  558.             if (ioResult != 0)
  559.                 ErrorWithStringArgument (CANT_CREATE_DIRECTORY, namePtr->name);
  560.     
  561.             qsort ((void *) namesArrayPtr,
  562.                          namesArrayLength / sizeof (char *),
  563.                          sizeof (char *),
  564.                          (int(__cdecl *) (const void *, const void *)) StrPtrCmp);
  565.             charPtrPtr = namesArrayPtr;
  566.             while (*charPtrPtr != NULL) {
  567.  
  568.                 charPtr = *charPtrPtr;
  569.                 if (!IgnoreFile (charPtr)) {
  570.                     namePtr = pathListPtr->nameData;
  571.                     count = pathListPtr->numberOfItems;
  572.                     do {
  573.                         suffixCharPtr = strrchr (namePtr->name, DIRECTORY_CHAR);
  574.                         if (suffixCharPtr == NULL || *(suffixCharPtr + 1) != '\0')
  575.                             AppendCharToPathName (namePtr->name, DIRECTORY_CHAR);
  576.                         AppendStringToPathName (namePtr->name, charPtr);
  577.                         count --;
  578.                         namePtr++;
  579.                     } while (count > 0);
  580.  
  581.                     MergePaths (pathListPtr);
  582.  
  583.                     namePtr = pathListPtr->nameData;
  584.                     count = pathListPtr->numberOfItems;
  585.                     do {
  586.                         RemoveNameSuffix (namePtr->name);
  587.                         count --;
  588.                         namePtr++;
  589.                     } while (count > 0);
  590.                 }
  591.     
  592.                 nextCharPtrPtr = charPtrPtr + 1;
  593.                 while (*nextCharPtrPtr != NULL && strcmp (charPtr, *nextCharPtrPtr) == 0)
  594.                     nextCharPtrPtr++;
  595.                 charPtrPtr = nextCharPtrPtr;
  596.             }
  597.             free (namesArrayPtr);
  598.             break;
  599.         case (BINARY_TYPE):
  600.         case (TEXT_TYPE):
  601.             isBinaryFile = lastFileType == BINARY_TYPE;
  602.             collision = FALSE;
  603.             incompleteMerge = FALSE;
  604.             /*
  605.              * Allocate  the space necessary for all the files.
  606.              */
  607.             fileModeCharPtr = "r";
  608.             if (isBinaryFile)
  609.                 fileModeCharPtr = "rb";
  610.             namePtr = pathListPtr->nameData;
  611.             filePtr = xmalloc (sizeof (struct file_data));
  612.             bzero (filePtr, sizeof (struct file_data));
  613.             filePtr->namePtr = namePtr->name;
  614.             filePtr->desc = fopen (namePtr->name, fileModeCharPtr);
  615.             if (filePtr->desc == NULL /* i.e. root doesn't exist*/)
  616.                 incompleteMerge = ADDED;
  617.             fileListPtr = filePtr;
  618.             namePtr++; /* Skip over the root file */
  619.             /*
  620.              * Open all the files.
  621.              */
  622.             version = 1;
  623.             do {
  624.                 fileDescriptor = fopen (namePtr->name, fileModeCharPtr);
  625.                 if (fileDescriptor == NULL) {
  626.                     if (!incompleteMerge)
  627.                         incompleteMerge = REMOVED;
  628.                 } else {
  629.                     filePtr->nextFilePtr = xmalloc (sizeof (struct file_data));
  630.                     filePtr = filePtr->nextFilePtr;
  631.                     bzero (filePtr, sizeof (struct file_data));
  632.                     filePtr->namePtr = namePtr->name;
  633.                     filePtr->desc = fileDescriptor;
  634.                     filePtr->version = version;
  635.                 }
  636.                 version ++;
  637.                 namePtr++;
  638.             } while (version < sourceFileCount);
  639.             /*
  640.              * When we have not removed the file then it needs to be kept in the destination.
  641.              * Note that namePtr points to the destination file,
  642.              */
  643.             if (incompleteMerge != REMOVED) {
  644.                 /*
  645.                  * If the root exists and the type of files are text we have the
  646.                  * typical case of merging one or more files into the root.
  647.                  */
  648.                 if (fileListPtr->desc != NULL && lastFileType == TEXT_TYPE)    {
  649.                     destinationFileDescriptor = CreateTypedFile (namePtr->name, isBinaryFile);
  650.                     lastFileNamePtr = namePtr->name;
  651.                     collision = MergeFiles (fileListPtr, destinationFileDescriptor);
  652.                     (void) fclose (destinationFileDescriptor);
  653.                     destinationFileDescriptor = NULL;
  654.                     fileAttributes = RESOURCE_MASK;
  655.                     if (collision == -1) {
  656.                         fileAttributes |= DATE_MASK;
  657.                         collision = FALSE;
  658.                     }
  659.                     if (collision) {
  660.                         (void) strcpy (collisionName, namePtr->name);
  661.                         PrependStringToPathName (collisionName, COLLISION_PREFIX);
  662.                         lastFileNamePtr = collisionName;
  663.                         /*
  664.                          * Remove a file with the collisionName and ignore the result
  665.                          * in case the file doesn't exist.
  666.                          */
  667.                         (void) remove (collisionName);
  668.                         ioResult = rename (namePtr->name, collisionName);
  669.                         if (ioResult != 0)
  670.                             Error (UNEXPECTED_IO_ERROR);
  671.                     }
  672.                     /*
  673.                    * When CopyFileAttributes copies the date it requires that the destination file
  674.                      * be closed, otherwise the date is instead set to the time the file is closed.
  675.                      */
  676.                     CopyFileAttributes (fileListPtr->namePtr, lastFileNamePtr, fileAttributes);
  677.                 } else {
  678.                     /*
  679.                      * Read in all the files
  680.                      */
  681.                     filePtr = fileListPtr;
  682.                     while (filePtr != NULL) {
  683.                         if (filePtr->desc != NULL) {
  684.  
  685.                             ioResult = fseek (filePtr->desc, 0, SEEK_END);
  686.                             if (ioResult != 0)
  687.                                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, filePtr->namePtr);
  688.     
  689.                             filePtr->bufsize = (unsigned int) ftell (filePtr->desc);
  690.                             if (filePtr->bufsize == -1)
  691.                                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, filePtr->namePtr);
  692.     
  693.                             ioResult = fseek (filePtr->desc, 0, SEEK_SET);
  694.                             if (ioResult != 0)
  695.                                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, filePtr->namePtr);
  696.     
  697.                             filePtr->buffer = (char *) xmalloc (filePtr->bufsize);    
  698.                             /*
  699.                              * We reset bufsize to the actual number of characters in the buffer rather
  700.                              * than the size malloced.  They may differ when they are text files when,
  701.                              * for example, text files store end of lines as more than one character.
  702.                              */
  703.                             filePtr->buffered_chars = fread (filePtr->buffer, 1, filePtr->bufsize, filePtr->desc);
  704.                             if (ferror (filePtr->desc) != 0)
  705.                                 ErrorWithStringArgument (UNEXPECTED_IO_ERROR, filePtr->namePtr);
  706.                             if (filePtr->buffered_chars != filePtr->bufsize) {
  707.                                 filePtr->bufsize = filePtr->buffered_chars;
  708.                                 filePtr->buffer = (char *) xrealloc (filePtr->buffer, filePtr->bufsize + 2);
  709.                             }
  710.                         }
  711.                         filePtr = filePtr->nextFilePtr;
  712.                     }
  713.                     /*
  714.                      * If one or fewer files differ from the base then take the modified file or base
  715.                      * respectively.  This situation occurs only for binary files since text files are
  716.                      * merged.
  717.                      */
  718.                     if (fileListPtr->desc != NULL) {
  719.                         filesThatDifferFromBase = 0;
  720.                         filePtr = fileListPtr;
  721.                         baseBufsize = filePtr->bufsize;
  722.                         baseBuffer = filePtr->buffer;
  723.                         fileWithDataPtr = filePtr;
  724.                         filePtr = filePtr->nextFilePtr;
  725.                         while (filePtr != NULL && filesThatDifferFromBase <= 1) {
  726.                             if (filePtr->desc != NULL) {                    
  727.                                 if ( (baseBufsize != filePtr->bufsize) ||
  728.                                          memcmp (baseBuffer, filePtr->buffer, filePtr->bufsize) != 0 ) {
  729.                                     filesThatDifferFromBase += 1;
  730.                                     fileWithDataPtr = filePtr;
  731.                                 }
  732.                             }
  733.                             filePtr = filePtr->nextFilePtr;
  734.                         }
  735.                         if (filesThatDifferFromBase <= 1)
  736.                             goto CopyFileWithData;
  737.                         else {
  738.                             collision = TRUE;
  739.                             goto MotherOfAllCollisions;
  740.                         }
  741.                     } else {
  742.                         /*
  743.                          * We don't have a base.  If all the copies are equal then keep one
  744.                          * of the copies.
  745.                          */
  746.                         fileWithDataPtr = NULL;
  747.                         filePtr = fileListPtr;
  748.                         while (filePtr != NULL) {
  749.                             if (filePtr->desc != NULL) {
  750.                                 if (fileWithDataPtr == NULL)
  751.                                     fileWithDataPtr = filePtr;
  752.                                 else
  753.                                     if ( (fileWithDataPtr->bufsize != filePtr->bufsize) ||
  754.                                              memcmp (fileWithDataPtr->buffer, filePtr->buffer, filePtr->bufsize) != 0 ) {
  755.                                         collision = TRUE;
  756.                                         break;
  757.                                     }
  758.                             }
  759.                             filePtr = filePtr->nextFilePtr;
  760.                         }
  761.  
  762.                         if (!collision) {
  763.                             /*
  764.                              * Copy the file pointed to by fileWithDataPtr to the destination.
  765.                              */
  766.     CopyFileWithData:
  767.                             destinationFileDescriptor = CreateTypedFile (namePtr->name, isBinaryFile);
  768.                             fwrite (fileWithDataPtr->buffer,
  769.                                             sizeof (char),
  770.                                             fileWithDataPtr->bufsize,
  771.                                             destinationFileDescriptor);
  772.                             (void) fclose (destinationFileDescriptor);
  773.                             destinationFileDescriptor = NULL;
  774.                             if (fileListPtr->desc != NULL /*i.e. base exists */)
  775.                                 CopyFileAttributes (fileListPtr->namePtr, namePtr->name, RESOURCE_MASK | DATE_MASK);
  776.                             else
  777.                                 CopyFileAttributes (fileWithDataPtr->namePtr, namePtr->name, RESOURCE_MASK);
  778.                         } else {
  779.     MotherOfAllCollisions:
  780.                             /*
  781.                              * Copy all the modified files to the destination and indicate a collision.
  782.                              */
  783.                             filePtr = fileListPtr->nextFilePtr;
  784.                             while (filePtr != NULL) {
  785.                                 strcpy (collisionName, namePtr->name);
  786.                                 sprintf (versionString, "%s%d", COLLISION_PREFIX, filePtr->version);
  787.                                 PrependStringToPathName (collisionName, versionString);
  788.                                 destinationFileDescriptor = CreateTypedFile (collisionName, isBinaryFile);
  789.                                 fwrite (filePtr->buffer, sizeof (char), filePtr->bufsize, destinationFileDescriptor);
  790.                                 (void) fclose (destinationFileDescriptor);
  791.                                 destinationFileDescriptor = NULL;
  792.                                 CopyFileAttributes (filePtr->namePtr, collisionName, RESOURCE_MASK | DATE_MASK);
  793.                                 filePtr = filePtr->nextFilePtr;
  794.                             }
  795.                         }
  796.                     }
  797.                 }
  798.             }
  799.             fileTypeString = "           " ;
  800.             if (lastFileType == BINARY_TYPE)
  801.                 fileTypeString = "BINARY FILE";
  802.  
  803.             collisionFormatString = "         ";
  804.             if (collision)
  805.                 collisionFormatString = "COLLISION";
  806.  
  807.             incompleteMergeString =   "       ";
  808.             if (incompleteMerge == ADDED)
  809.                 incompleteMergeString = "ADDED  ";
  810.             else if (incompleteMerge == REMOVED)
  811.                 incompleteMergeString = "REMOVED";
  812.  
  813.             printf ("%s  %s  %s  %s\n", collisionFormatString, incompleteMergeString, fileTypeString, namePtr->name);
  814.             break;
  815.     }
  816.  
  817.     filePtr = fileListPtr;
  818.     while (filePtr != NULL) {
  819.         nextFilePtr = filePtr->nextFilePtr;
  820.         freeFile (filePtr);
  821.         free (filePtr);
  822.         filePtr = nextFilePtr;
  823.     }
  824. }
  825.  
  826.     void
  827.     PrependStringToPathName (char *destinationCharPtr, const char *prefixCharPtr)
  828.     {
  829.         char *fileNamePtr;
  830.         int        prefixLength;
  831.  
  832.         fileNamePtr = strrchr (destinationCharPtr, DIRECTORY_CHAR);
  833.         prefixLength = strlen (prefixCharPtr);
  834.         if (strlen (destinationCharPtr) + prefixLength > FILENAME_MAX) {
  835.             if (fileNamePtr == NULL)
  836.                 fprintf (stderr, "%s%s\n", prefixCharPtr, destinationCharPtr); 
  837.             else {
  838.                 *fileNamePtr = '\0';
  839.                 fprintf (stderr, "%s%c%s\n", destinationCharPtr, DIRECTORY_CHAR, fileNamePtr + 1);
  840.                 *fileNamePtr = DIRECTORY_CHAR;
  841.             }
  842.             Error (PATH_TOO_LONG);
  843.         }
  844.         if (fileNamePtr == NULL)
  845.             fileNamePtr = destinationCharPtr;
  846.         else
  847.             fileNamePtr++;
  848.         (void) memmove (fileNamePtr + prefixLength, fileNamePtr, strlen (fileNamePtr) + 1);
  849.         (void) memcpy (fileNamePtr, prefixCharPtr, prefixLength);
  850.     }
  851.  
  852.  
  853. void
  854. PrintEndFileName (FILE *outFilePtr)
  855. {
  856.     fprintf (outFilePtr, "#####\n");
  857. }
  858.  
  859. void
  860. PrintLines (FILE *outFilePtr, struct file_data *filePtr, int startLineNumber, register int endLineNumber)
  861. {
  862.     struct line_def     *linePtr;
  863.     register int            maxLineNumber;
  864.     register int            numberOfLines;
  865.  
  866.     linePtr = &filePtr->linbuf[startLineNumber];
  867.     maxLineNumber = filePtr->buffered_lines;
  868.     if (endLineNumber > maxLineNumber)
  869.         endLineNumber = maxLineNumber;
  870.     numberOfLines = endLineNumber - startLineNumber;
  871.     while (numberOfLines > 0) {
  872.         fwrite (linePtr->text, sizeof (char), linePtr->length, outFilePtr);
  873.         linePtr ++;
  874.         numberOfLines --;
  875.     }
  876. }
  877.  
  878. void
  879. PrintStartFileName (FILE *outFilePtr, struct file_data *filePtr)
  880. {
  881.     fprintf (outFilePtr, "##### File: %s\n", filePtr->namePtr);
  882. }
  883.  
  884. int
  885. __cdecl StrPtrCmp (char **string0PtrPtr, char **string1PtrPtr)
  886. {
  887.      return (strcmp (*string0PtrPtr, *string1PtrPtr));
  888. }
  889.